package com.eddie.auction.dao;

import cn.hutool.core.util.StrUtil;
import com.eddie.auction.constant.TypeConstant;
import com.eddie.auction.entity.AuctionEntity;
import com.eddie.auction.utils.DataBaseUtil;
import lombok.Data;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Eddie
 * @date 2022/08/01 14:13
 **/
public class BaseDao {

    private final DataBaseUtil dataBaseUtil = DataBaseUtil.getInstance();

    public int executeUpdate(String sql, Object[] params) {
        //影响行数
        int result = 0;
        PreparedStatement stmt = null;
        try {
            stmt = dataBaseUtil.getConnection().prepareStatement(sql);
            if (!(params == null || params.length == 0)) for (int i = 0; i < params.length; i++) {
                //将？的占位符放入具体的参数
                stmt.setObject(i + 1, params[i]);
            }
            //放入执行结束后返回的影响行数
            result = stmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            //关闭声明
            dataBaseUtil.closeStatement(stmt, null);
        }
        return result;
    }

    public int executeUpdate(String sql, Object entity) {
        Map<String, Object[]> mappingResult = handleParameterMapping(sql, entity);
        //直接获取到该map的Set集合的第一项
        Map.Entry<String, Object[]> next = mappingResult.entrySet().iterator().next();
        //调用执行方法去执行
        return executeUpdate(next.getKey(), next.getValue());
    }

    protected Map<String, Object[]> handleParameterMapping(String sql, Object entity) {

        HashMap<String, Object[]> mappingResult = new HashMap<>();
        List<String> placeholders = new ArrayList<>();
        int offset = 0;
        while (true) {
            //#{ 要搜索的字符串 offset 搜索起始位置
            int start = sql.indexOf("#{", offset);
            //如果查到了位置则不为-1
            if (start >= offset) {
                //获取该次占位符的尾部
                int end = sql.indexOf("}", start);
                //获取占位符的名字并放入占位符集合中
                placeholders.add(sql.substring(start + 2, end));
                //设置end+1目的为了让下一次循环能够从该次占位符的末端尾部+1的索引开始找
                offset = end + 1;
            } else {
                break;
            }
        }
        //表示至少找到了一个占位符
        if (placeholders.size() > 0) {
            //参数列
            Object[] params = new Object[placeholders.size()];
            for (int i = 0; i < placeholders.size(); i++) {
                String placeholder = placeholders.get(i);
                //修改第一个所找到的占位符位置的字符为？
                sql = sql.replaceFirst("#\\{" + placeholder + "}", "?");
                try {
                    // getnickname => getNickname
                    Method method = entity.getClass().getMethod("get"
                            + placeholder.substring(0, 1).toUpperCase()
                            + placeholder.substring(1));
                    //调用该类的get对应方法获取值
                    Object param = method.invoke(entity);
                    //添加到参数列
                    params[i] = param;
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                }

            }
            //返回解析后的sql及条件数组
            mappingResult.put(sql, params);
        } else {
            //直接设置为空参
            mappingResult.put(sql, new Object[]{});
        }
        return mappingResult;
    }

    /**
     * 查询操作
     *
     * @param clz    类型
     * @param sql    sql语句
     * @param params 参数数组
     * @param <T>    泛型类
     * @return 查询结果
     */
    public <T> List<T> executeQuery(Class<T> clz, String sql, Object[] params) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = dataBaseUtil.getConnection().prepareStatement(sql);
            if (!(params == null || params.length == 0)) {
                for (int i = 0; i < params.length; i++) {
                    stmt.setObject(i + 1, params[i]);
                }
            }
            rs = stmt.executeQuery();
            return handleResultMapping(clz, rs);
        } catch (SQLException | InvocationTargetException | NoSuchMethodException | InstantiationException |
                 IllegalAccessException e) {
            throw new RuntimeException(e);
        } finally {
            dataBaseUtil.closeStatement(stmt, rs);
        }
    }

    public <T> List<T> executeQuery(Class<T> clz, String sql, Object entity) {
        Map<String, Object[]> mappingResult = handleParameterMapping(sql, entity);
        Map.Entry<String, Object[]> entry = mappingResult.entrySet().iterator().next();
        return executeQuery(clz, entry.getKey(), entry.getValue());

    }

    /**
     * 查询结果解析
     *
     * @param clz 封装类
     * @param rs  结果集
     * @param <T> 类型
     * @return 类集合
     */
    protected <T> List<T> handleResultMapping(Class<T> clz, ResultSet rs) throws SQLException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //如果返回的结果是数字
        if (clz.equals(Integer.class)) {
            List<Integer> result = new ArrayList<>();
            while (rs.next()) result.add(rs.getInt(1));
            return (List<T>) result;
        }
        //获得ResultSet的元数据
        ResultSetMetaData metaData = rs.getMetaData();
        //获得结果集的列数
        int columnCount = metaData.getColumnCount();
        //存储每一列对应的Java对象的Setter
        Method[] methods = new Method[columnCount];
        //存储setter方法的参数类型
        Class<?>[] types = new Class[columnCount];
        for (int i = 0; i < columnCount; i++) {
            //获取查询结果的列标题
            String columnLabel = metaData.getColumnLabel(i + 1);
            //获取该列数据库类型名
            String columnTypeName = metaData.getColumnTypeName(i + 1);
            try {
                //StrUtil.toCamelCase("set" +"_"+ columnLabel) 将列名转换为对应的set方法
                methods[i] = clz.getMethod(StrUtil.toCamelCase("set" +"_"+ columnLabel),TypeConstant.getJavaType(columnTypeName));
                types[i] = TypeConstant.getJavaType(columnTypeName);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                methods[i] = null;
                types[i] = null;
            }
        }
        List<T> result = new ArrayList<>();
        T instance;
        //从结果集取出每一列的值，调用Java对象中对应的setter赋值
        while (rs.next()) {
            instance = clz.getDeclaredConstructor().newInstance();
            for (int i = 0; i < columnCount; i++) {
                if (methods[i] == null) {
                    continue;
                }
                //日期类需要特殊处理
                if (types[i].equals(java.time.LocalDateTime.class)) {
//                    LocalDate localDate = Instant.ofEpochMilli(rs.getTimestamp(i + 1).getTime()).atZone(ZoneOffset.ofHours(8)).toLocalDate();
                    LocalDateTime localDateTime = Instant.ofEpochMilli(rs.getTimestamp(i + 1).getTime()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
                    methods[i].invoke(instance, localDateTime);
                } else {
                    methods[i].invoke(instance, rs.getObject(i + 1, types[i]));
                }
            }
            result.add(instance);
        }
        return result;
    }
}
